////////////////////////////////////////////////////////////////////
// File:	Sphere.cpp
// Purpose: Creates a perfect sphere by using a process called "subdivision"
//			this class also allows you to access it's triangles or normals.
//			This class creates a sphere on the fly.
// Author:	Jay Baird
////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <gl.h>

#include "Sphere.h"

using MeshComponent::Triangle;

#define X .525731112119133606f
#define Z .850650808352039932f

static const vec3 vdata[12] = 
{    
    vec3(-X, 0.0, Z), vec3(X, 0.0, Z), vec3(-X, 0.0, -Z), vec3(X, 0.0, -Z),    
    vec3(0.0, Z, X), vec3(0.0, Z, -X), vec3(0.0, -Z, X), vec3(0.0, -Z, -X),    
    vec3(Z, X, 0.0), vec3(-Z, X, 0.0), vec3(Z, -X, 0.0), vec3(-Z, -X, 0.0) 
};

static const Triangle triangles[20] =
{
	Triangle(0, 4, 1), Triangle(0, 9, 4), Triangle(9, 5, 4), 
	Triangle(4, 5, 8), Triangle(4, 8, 1), Triangle(8, 10, 1),
	Triangle(8, 3, 10), Triangle(5, 3, 8), Triangle(5, 2, 3),
	Triangle(2, 7, 3), Triangle(7, 10, 3), Triangle(7, 6, 10),
	Triangle(7, 11, 6), Triangle(11, 0, 6), Triangle(0, 1, 6),
	Triangle(6, 1, 10), Triangle(9, 0, 11), Triangle(9, 11, 2),
	Triangle(9, 2, 5), Triangle(7, 2, 11)
};

void Sphere::CreateTriangle(const vec3 &a, const vec3 &b, const vec3 &c, 
						    unsigned div, float radius)
{
	if(div == 0) 
	{
		Triangle newTriangle;
		VertChunk newVert1, newVert2, newVert3;

		newVert1.normal = a;
		newVert2.normal = b;
		newVert3.normal = c;

		newVert1.vertex = a * radius;
		newVert2.vertex = b * radius;
		newVert3.vertex = c * radius;

		short i1 = -1, i2 = -1, i3 = -1;

		// Build unique vertex list
		FindVertices(newVert1, newVert2, newVert3, i1, i2, i3);

		if(i1 == -1)
		{
			newTriangle.index[0] = (unsigned short)m_verts.size();
			m_verts.push_back(newVert1);
		}
		else newTriangle.index[0] = i1;

		if(i2 == -1)
		{
			newTriangle.index[1] = (unsigned short)m_verts.size();
			m_verts.push_back(newVert2);
		}
		else newTriangle.index[1] = i2;

		if(i3 == -1)
		{
			newTriangle.index[2] = (unsigned short)m_verts.size();
			m_verts.push_back(newVert3);
		}
		else newTriangle.index[2] = i3;

		m_triangles.push_back(newTriangle);
	} 
	else 
	{
		vec3 ab, ac, bc;

		for(int i = 0; i < 3; i++) 
		{
			ab[i] = (a.V()[i] + b.V()[i]) / 2;
			ac[i] = (a.V()[i] + c.V()[i]) / 2;
			bc[i] = (b.V()[i] + c.V()[i]) / 2;
		}

		Normalize(ab); 
		Normalize(ac); 
		Normalize(bc);

		CreateTriangle(a, ab, ac, div - 1, radius);
		CreateTriangle(b, bc, ab, div - 1, radius);
		CreateTriangle(c, ac, bc, div - 1, radius);
		CreateTriangle(ab, bc, ac, div - 1, radius);  //<--Comment this line and sphere looks really cool!
	}
}

void Sphere::FindVertices(const VertChunk& vertex1, const VertChunk& vertex2, const VertChunk& vertex3,
						  short& i1, short& i2, short& i3)
{
	// Go through the vertex list and search and see if its already there...
	unsigned vertexCount = (unsigned)this->m_verts.size();
	for (unsigned i = 0; i < vertexCount; i++)
	{
		if(i1 == -1 && vertex1 == m_verts[i])
			i1 = i;
		if(i2 == -1 && vertex2 == m_verts[i])
			i2 = i;
		if(i3 == -1 && vertex3 == m_verts[i])
			i3 = i;

		if(i1 != -1 && i2 != -1 && i3 != -1) return ;
	}
}

void Sphere::CreateSphere(unsigned int numDivisions, float radius)
{
    glBegin(GL_TRIANGLES);

    for(int i = 0; i < 20; i++)
	{
        CreateTriangle(vdata[triangles[i].index[0]], 
					 vdata[triangles[i].index[1]], 
					 vdata[triangles[i].index[2]], 
					 numDivisions, radius);
	}

    glEnd();
}